package source.model;

import source.model.skill.Skill;
import source.model.type.GameObjectType;
import source.model.type.UnitType;
import source.model.Position;
import source.events.action.*;
import source.view.MainScreen;
import source.model.skill.SkillObserve;
import java.util.Vector;
import source.model.bonus.*;

public class Unit extends Entity{

	private MoveDirection md;
	private static int nextId = 0;
	private UnitType uType;
	private Movement move;
	private Health health;
	private Item itemCargoed;
	private Vector<Skill> skillSet;
	private boolean isOnMission;
	private int odBonus;
	private int ddBonus;
	private int armorBonus;
	private int rangeBonus;
	private float speedBonus;
	
	public Unit( UnitType u ) {
		super(GameObjectType.UNIT, nextId++);
		uType = u;
		move = new Movement( u.mvRate() );
		health = new Health(u.health());
		skillSet = new Vector<Skill>();
		
		Skill[] skeels = u.skillSet();
		for (Skill s:skeels) {
			skillSet.add(s);
		}
		
		odBonus=0;
		ddBonus=0;
		armorBonus=0;
		rangeBonus=0;
		speedBonus=(float)0.0;
	}
	
	public boolean getMissionFlag()
	{
		return isOnMission;
	}
	
	public void setMissionFlag(boolean b)
	{
		isOnMission = b;
	}
	public void setFacing(MoveDirection md){
		this.md=md;
	}
	
	public MoveDirection getFacing(){
		return md;
	}

	public boolean isPlayerOwned() {
		return true;
	}
	
	public UnitType getType()
	{
		return uType;
	}
	public int getUpkeep(){
		return uType.upkeep();
	}
	public Action newTurn()
	{
		return new UnitNewTurn(objectID());
	}
	public int mvRate() { 
		return (int)move.rate;
	}
	
	public float mvLeft() {
		return move.left;
	}
	
	public void applyBonuses(Bonus bonus, DataHandler dh){
		bonus.apply(dh);
	}
	
	/**
	 * Sets the unit's remaining movement amount to zero for this turn.
	 * The unit will have its normal movement amount startin on the next
	 * turn.
	 * 
	 * precondition: none
	 * postcondition: the unit will have 0.0 movement remaining
	 */
	public void clearMovement()
	{
		move.setLeft(0.0f);
	}

	public void resetMovement(){
		move.reset();
	}
	
	public boolean move( float mvPoints ) {
		return move.move( mvPoints * (1-speedBonus));
	}
	
	/**
	 * This method is used to mark the object as having been forcibly moved (i.e.,
	 * Unit teleported using Area Effect) and that it's possition needs to be changed.
	 * @param x new x-coordinate
	 * @param y new y-coordinate
	 * @return true if object allowed the forced change, false otherwise
	 */
	public boolean changePosition( int x, int y ){
		//TODO:  implement method
		return true;
	}
	
	/**
	 * Queries the object to find out if the changePositionFlag is set, meaning
	 * the object's position in pMap must be updated
	 * @return true if flag set; false otherwise
	 */
	public boolean changePositionFlag(){
		//TODO:  implement method
		return false;
	}
	
	public Position newPosition(){
		//TODO: implement method
		return new Position(0, 0);
	}
	
	/**
	 * Once the DataHandler resets the position of the object, it calls this method to
	 * let the object know that it is now no longer in a "Forced Position Change" 
	 * state
	 * @return true if the object reset it's flag, false otherwise
	 */
	public boolean resetPositionChangedFlag(){
		//TODO:  implement method
		return true;
		
	}
	
	public void modODBonus(int odBonus){
		this.odBonus=odBonus;
	}
	
	public void modDDBonus(int ddBonus){
		this.ddBonus=ddBonus;
	}
	
	public void modArmorBonus(int armorBonus){
		this.armorBonus=armorBonus;
	}
	
	public void modHealthBonus(int healthBonus){
		health.increaseMaxHealth(healthBonus);
	}
	
	public void modSpeedBonus(float speedBonus){
		this.speedBonus=speedBonus;
	}
	
	public void modRangeBonus(int rangeBonus){
		this.rangeBonus=rangeBonus;
	}
	
	private class Movement {
		private float rate, left;	/* rate = base movement rate,
										left = remaining movement */
		
		public Movement( int mvRate ) {
			rate = mvRate;
			left = mvRate;
		}
		
		public void setRate( float r ) {
			rate = r;
		}
		
		public void setLeft( float f)
		{
			left = f;
		}
		
		public float getLeft()
		{
			return left;
		}
		
		public void reset() {
			left = rate;
		}
		
		public boolean move( float dec ) {
			if ( left <= 0 ) return false;
			
			left -= dec;
			return true;
		}
	}
	
	public void addItem(Item item){
		this.itemCargoed=item;
	}
	
	public Item getItem(){
		return itemCargoed;
	}
	
	public void removeItem(){
		itemCargoed=null;
	}
	
	public int getView(){
		return uType.view();
	}
	
	public String getName(){
		return uType.getName();
	}
	
	public Skill getSkill(String wantedSkill){
		
		if(skillSet.isEmpty())
		{
			//MainScreen.writeToConsole("Unit:  SkillSet is empty");
			return null;
		}
		for (Skill skill:skillSet){
			if (skill.skillName().equals(wantedSkill)){
				return skill;
			}
		}
		//MainScreen.writeToConsole("Unit: " + wantedSkill + " not found");
		return null;
	}
	
	public int armor(){
		return uType.armor()+armorBonus;
	}
	
	public int oDamage(){
		return uType.oDamage()+odBonus;
	}
	
	public int dDamage(){
		return uType.dDamage()+ddBonus;
	}
	
	public int currentHealth(){
		return health.currentHealth();
	}
	
	public int maxHealth(){
		return health.maxHealth();
	}
	
	/**
	 * 
	 * @param modAmmount
	 * @return
	 * Returns true if alive, else returns false
	 */
	public boolean modHealth(int modAmmount){
		if (modAmmount < 0) modAmmount = (Math.abs(modAmmount) <= armor() ? 0 : modAmmount + armor());
		
		boolean alive = health.modHealth(modAmmount);
		if (!alive){
			invalidate();
		}
		return alive;
	}
	
	public boolean modHealthAreaEffect(int modAmmount){
		
		boolean alive = health.modHealth(modAmmount);
		if (!alive){
			invalidate();
		}
		return alive;
	}
	
	public java.util.Hashtable<String, String> getViewData()
	{
		java.util.Hashtable<String, String> viewData = super.getViewData();
		
		viewData.put("Unit Type", uType.getName());
		viewData.put("Maximum Health", Integer.toString(this.maxHealth()));
		viewData.put("Minimum Range", Integer.toString(uType.minRange()));
		viewData.put("Maximum Range", Integer.toString(uType.maxRange()));
		viewData.put("Movement Rate", Integer.toString(uType.mvRate()));
		viewData.put("View Distance", Integer.toString(uType.view()));
		viewData.put("Current Health", Integer.toString(this.currentHealth()));
		viewData.put("Moves Remaining", Float.toString(move.getLeft()));
		
		String skills = "";
		for(Skill current : uType.skillSet())
		{
			skills += current.skillName() + ", ";
		}
		
		if (skills != "") skills = skills.substring(0, skills.length() - 2);
		else skills = "none";
		
		viewData.put("Skills", skills);
		
		return viewData;
	}
}
